#include "app_device.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include "log/log.h"
#include "app_pool.h"
#include "app_common.h"

#define BUFFER_SIZE 1024

// 将数据写入设备的任务函数
static int write_task_fun(void *arg)
{
    Device *device = (Device *)arg;

    // 从设备写缓存中读取一段完整的数据
    char data[256];
    app_buffer_read(device->write_buffer, data, 3);
    app_buffer_read(device->write_buffer, data+3, data[1] + data[2]);
    int len = 3 + data[1] + data[2]; // 读取到的数据长度
    // 调用由具体设备实现的pre_write函数对数据进行处理
    if (device->funcs->pre_write)
    {
        len = device->funcs->pre_write(device, data, len);
    }

    // 保证两次向设备写数据之间时间间隔足够=》 让前一次写完
    long time_distance = app_common_getCurrentTime() - device->last_write_time;
    if (time_distance <100)
    {
        usleep((100 - time_distance)*1000);
    }

    // while(app_common_getCurrentTime() - device->last_write_time < 100)
    // {
    //     usleep(1000);
    // }

    // 将数据写入设备
    write(device->fd, data, len);
    // 更新last_write_time
    device->last_write_time = app_common_getCurrentTime();
}

/**
 * @breif 发送数据到远程服务器的任务函数
 */
static int send_msg(void *arg)
{
    Device *device = (Device *)arg;
    // 从device读缓存中取出一套完整数据
    char data[256];
    app_buffer_read(device->read_buffer, data, 3); // 读取前3个  conn_type/id_len/msg_len
    app_buffer_read(device->read_buffer, data + 3, data[1] + data[2]); // id和msg
    // 发送到远程服务器
    return device->funcs->send_msg(device, data, 3 + data[1] + data[2]);
}

/**
 * @brief 不断读取设备文件数据的线程函数  设备读线程
 */
static void * read_thread_fun (void *arg)
{
    char buf[1024];
    Device *device = (Device *)arg;
    while(device->running){
        //读取设备文件中的数据到buf数组
        int len = read(device->fd, buf, sizeof(buf));
        // 如果指定读后处理的函数，调用它处理
        if (device->funcs->post_read)
        {
            len = device->funcs->post_read(device, buf, len);
        }
        // 将数据保存read_buffer
        app_buffer_write(device->read_buffer, buf, len);

        // 注册一个发送任务到任务队列
        app_pool_registerTask(send_msg, device);
        // send_msg(device); // 不能直接让设备读线程发消息到远程
    }

}


/**
 * @brief 初始化设备
 */
Device *app_device_init(char *filename)
{
    // 初始化Device结构体
    Device *device = (Device *)malloc(sizeof(Device));

    // 初始化设备属性
    device->filename = filename;
    device->fd = open(filename, O_CREAT|O_RDWR, 0644);
    if (device->fd < 0)
    {
        log_error("open file %s failed");
        free(device);
        return NULL;
    }
    device->type = DEV_GENERAL;
    device->conn_type = CONN_NONE;
    device->read_buffer = app_buffer_init(BUFFER_SIZE);
    device->write_buffer = app_buffer_init(BUFFER_SIZE);
    device->running = 0;
    device->funcs = malloc(sizeof(struct DeviceFuncs));
    device->last_write_time = 0;

    return device;
}

/**
 * @brief 启动设备（启动设备读线程去不断读取文件中的数据）
 */
int app_device_start(Device *device)
{
    // 如果设备读线程已经在运行，则直接返回
    if (device->running)
    {
        log_debug("device %s is already running");
        return 0;
    }

    // 将是否正在运行的标识改为1
    device->running = 1; // 在启动分线程前
    // 启动设备读线程
    if (pthread_create(&device->read_thread, NULL, read_thread_fun, device) == -1)
    {
        log_error("create read thread failed");
        device->running = 0; // 将标识改回0
        return -1;
    }

    return 0;
}

/**
 * @brief 停止设备（取消设备读线程）
 */
int app_device_stop(Device *device)
{
    if (device->running == 0)
    {
        log_debug("device %s is not running");
        return 0;
    }

    // 将是否正在运行的标识改为0
    device->running = 0;
    // 取消设备读线程
    pthread_cancel(device->read_thread);
    pthread_join(device->read_thread, NULL);

    return 0;
}

/**
 * @brief 关闭设备
 */
void app_device_close(Device *device)
{
    // 关闭文件
    close(device->fd);

    // 释放buffer
    app_buffer_free(device->read_buffer);
    app_buffer_free(device->write_buffer);

    // 释放funcs
    free(device->funcs);

    // 释放device
    free(device);
}


/**
 * 接收消息
 */
int app_device_recvMsg(Device *device, char *data, int len)
{
    // 将数据保存设备的写缓存
    app_buffer_write(device->write_buffer, data, len);

    // 注册一个写任务来将数据写入设备文件
    app_pool_registerTask(write_task_fun, device);
}
